﻿using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Notoric.Commanding;
using Notoric.Commanding.CommandBase;

namespace Notoric.Gui.Windows
{
    /// <summary>
    /// Interaction logic for NewCompositionDialog.xaml
    /// </summary>
    public partial class NewCompositionDialog : Window
    {
        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="NewCompositionDialog"/> class.
        /// </summary>
        public NewCompositionDialog()
        {
            InitializeComponent();
            personsCommands = new List<UndoableCommandBase>();
            PART_Title.Focus();
        }

        #endregion

        #region Dependency Property Fields

        /// <summary>
        /// Identifies the <see cref="IsNewComposition"/> dependency property.
        /// </summary>
        public static readonly DependencyProperty IsNewCompositionProperty = DependencyProperty.Register(
            "IsNewComposition", typeof(bool), typeof(NewCompositionDialog), new PropertyMetadata(true)
            );

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets a value indicating whether the edited composition has just been created.
        /// </summary>
        /// <value>
        /// 	<see langword="true"/> if the composition has just been created; otherwise, <see langword="false"/>.
        /// 	Default value is <see langword="true"/>.
        /// </value>
        public bool IsNewComposition
        {
            get { return (bool)GetValue(IsNewCompositionProperty); }
            set { SetValue(IsNewCompositionProperty, value); }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Handles the Click event of the PART_OK control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PART_OK_Click(object sender, RoutedEventArgs e)
        {
            var composition = (ViewModel.Elements.Composition)DataContext;

            if (IsNewComposition)
            {
                if (PART_Instruments.SelectedItem == parts_BassStaff)
                    composition.AddSingleStaffPart("Bass", "bas.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                else if (PART_Instruments.SelectedItem == parts_BrassQuartet)
                {
                    composition.AddSingleStaffPart("1st Trompet in B", "Tr. B 1", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("2nd Trompet in B", "Tr. B 2", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Trombone", "Tromb.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                    composition.AddSingleStaffPart("Tuba", "Tub.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                }
                else if (PART_Instruments.SelectedItem == parts_BrassQuintet)
                {
                    composition.AddSingleStaffPart("Horn in F", "Horn", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("1st Trompet in B", "Tr. B 1", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("2nd Trompet in B", "Tr. B 2", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Trombone", "Tromb.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                    composition.AddSingleStaffPart("Tuba", "Tub.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                }
                else if (PART_Instruments.SelectedItem == parts_Guitar)
                    composition.AddSingleStaffPart("Guitar", "guit.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                else if (PART_Instruments.SelectedItem == parts_ChoirBarbershop)
                {
                    composition.AddSingleStaffPart("Tenor Lead", "T.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Baritone Bass", "BB.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                }
                else if (PART_Instruments.SelectedItem == parts_ChoirReduction)
                {
                    composition.AddSingleStaffPart("Soprano / Alto", "S, A", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Tenore / Bass", "T, B.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                }
                else if (PART_Instruments.SelectedItem == parts_ChoirSATB)
                {
                    composition.AddSingleStaffPart("Soprano", "S.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Alto", "A.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Tenore", "T", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Bass", "B.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                }
                else if (PART_Instruments.SelectedItem == parts_ChoirSATBOrgan)
                {
                    composition.AddSingleStaffPart("Soprano", "S.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Alto", "A.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Tenore", "T", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Bass", "B.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                    composition.AddOrganPart("Organ", "org.");
                }
                else if (PART_Instruments.SelectedItem == parts_ChoirSATBPiano)
                {
                    composition.AddSingleStaffPart("Soprano", "S.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Alto", "A.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Tenore", "T", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Bass", "B.", ViewModel.Elements.CWNotation.ClefType.F, 6);
                    composition.AddPianoPart("Piano", "pno.");
                }
                else if (PART_Instruments.SelectedItem == parts_Organ)
                    composition.AddOrganPart("Organ", "org.");
                else if (PART_Instruments.SelectedItem == parts_Piano)
                    composition.AddPianoPart("Piano", "pno.");
                else if (PART_Instruments.SelectedItem == parts_StringQuartet)
                {
                    composition.AddSingleStaffPart("Violin I", "Vln I", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Violin II", "Vln II", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddSingleStaffPart("Viola", "Vla.", ViewModel.Elements.CWNotation.ClefType.C, 3);
                    composition.AddSingleStaffPart("Violoncello", "Cello", ViewModel.Elements.CWNotation.ClefType.F, 6);
                }
                else if (PART_Instruments.SelectedItem == parts_VoiceKeyboard)
                {
                    composition.AddSingleStaffPart("Voice", "v.", ViewModel.Elements.CWNotation.ClefType.G, 2);
                    composition.AddPianoPart("Keyboard", "kb.");
                }

                composition.AddMeasures((int)PART_MeasuresCount.Value);

                UndoableCommandBase commit;
                composition.CommitChanges(out commit);
                if (commit != null)
                {
                    commit.UseStacks = false;
                    commit.Execute(null);
                }
            }
            else
            {
                UndoableCommandBase commit;
                composition.CommitChanges(out commit);
                UndoableMacroCommand macro = new UndoableMacroCommand(composition.UndoStack, composition.RedoStack)
                {
                    Name = "Edit Composition Properties"
                };
                if (commit != null)
                    macro.Commands.Add(commit);
                if (PART_MeasuresCount.Value > composition.MeasuresCount)
                {
                    macro.Commands.Add(new ViewModel.InsertMeasuresCommand(composition.UndoStack, composition.RedoStack)
                    {
                        Coda = false,
                        Composition = composition,
                        Count = (int)(PART_MeasuresCount.Value - composition.MeasuresCount)
                    }
                    );
                }
                else if (PART_MeasuresCount.Value < composition.MeasuresCount)
                {
                    if (ViewModel.Elements.MusicSection.HasRepresentations(composition, (ushort)(PART_MeasuresCount.Value + 1), (ushort)composition.MeasuresCount))
                    {
                        MessageBox.Show(
                            "The measures cannot be removed, since they are presented in one or more music sections. Please, remove these sections first.",
                            "Notoric",
                            MessageBoxButton.OK,
                            MessageBoxImage.Exclamation
                            );

                        return;
                    }

                    macro.Commands.Add(new ViewModel.RemoveMeasuresCommand(composition.UndoStack, composition.RedoStack)
                    {
                        Composition = composition,
                        Count = (int)(composition.MeasuresCount - PART_MeasuresCount.Value),
                        Start = (ushort)(PART_MeasuresCount.Value + 1)
                    }
                    );
                }
                if (macro.Commands.Count > 0 || personsCommands.Count > 0)
                    macro.Execute(null);

                macro.Commands.AddRange(personsCommands);
            }

            this.DialogResult = true;
        }

        /// <summary>
        /// Handles the Click event of the PART_Cancel control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PART_Cancel_Click(object sender, RoutedEventArgs e)
        {
            var composition = (ViewModel.Elements.Composition)DataContext;
            composition.AbortChanges();
            for (int i = personsCommands.Count - 1; i >= 0; --i)
                personsCommands[i].Execute(null);

            this.DialogResult = false;
        }

        /// <summary>
        /// Handles the Click event of the PART_AddLyricist control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PART_AddLyricist_Click(object sender, RoutedEventArgs e)
        {
            var composition = (ViewModel.Elements.Composition)DataContext;
            var cmd = new ViewModel.AddLyricistCommand(composition.UndoStack, composition.RedoStack)
            {
                Composition = composition,
                UseStacks = false
            };
            cmd.Execute(null);

            var dlg = new EditPersonDialog()
            {
                DataContext = cmd.CreatedLyricist
            };
            if (dlg.ShowDialog() == true)
            {
                personsCommands.Add(cmd);
                var cmdPerson = composition.UndoStack.Pop();
                cmdPerson.UseStacks = false;
                personsCommands.Add(cmdPerson);
            }
            else
                cmd.Execute(null);
        }

        /// <summary>
        /// Handles the Click event of the PART_RemoveLyricist control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PART_RemoveLyricist_Click(object sender, RoutedEventArgs e)
        {
            var lyricist = (ViewModel.Elements.Person)PART_Lyricists.SelectedItem;
            if (lyricist == null)
                return;
            var cmd = personsCommands.FirstOrDefault(c => c is ViewModel.AddLyricistCommand && ((ViewModel.AddLyricistCommand)c).CreatedLyricist == lyricist);
            if (cmd != null)
                personsCommands.Remove(cmd);
            else
            {
                cmd = new ViewModel.RemoveLyricistCommand(null, null)
                {
                    Lyricist = lyricist
                };
                personsCommands.Add(cmd);
            }

            cmd.Execute(null);
        }

        /// <summary>
        /// Handles the Click event of the PART_AddComposer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PART_AddComposer_Click(object sender, RoutedEventArgs e)
        {
            var composition = (ViewModel.Elements.Composition)DataContext;
            var cmd = new ViewModel.AddComposerCommand(composition.UndoStack, composition.RedoStack)
            {
                Composition = composition,
                UseStacks = false
            };
            cmd.Execute(null);

            var dlg = new EditPersonDialog()
            {
                DataContext = cmd.CreatedComposer
            };
            if (dlg.ShowDialog() == true)
            {
                personsCommands.Add(cmd);
                var cmdPerson = composition.UndoStack.Pop();
                cmdPerson.UseStacks = false;
                personsCommands.Add(cmdPerson);
            }
            else
                cmd.Execute(null);
        }

        /// <summary>
        /// Handles the Click event of the PART_RemoveComposer control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void PART_RemoveComposer_Click(object sender, RoutedEventArgs e)
        {
            var composer = (ViewModel.Elements.Person)PART_Composers.SelectedItem;
            if (composer == null)
                return;
            var cmd = personsCommands.FirstOrDefault(c => c is ViewModel.AddComposerCommand && ((ViewModel.AddComposerCommand)c).CreatedComposer == composer);
            if (cmd != null)
                personsCommands.Remove(cmd);
            else
            {
                cmd = new ViewModel.RemoveComposerCommand(null, null)
                {
                    Composer = composer
                };
                personsCommands.Add(cmd);
            }

            cmd.Execute(null);
        }

        /// <summary>
        /// Handles the Loaded event of the Window control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            if (IsNewComposition)
                PART_MeasuresCount.Value = 15;
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the commands executed for adding / removing persons.
        /// </summary>
        private List<UndoableCommandBase> personsCommands;

        #endregion
    }
}
